package com.wxapp.common.util.copy;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wxapp.common.util.check.CheckUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.BeansException;
import org.springframework.lang.Nullable;
import java.beans.PropertyDescriptor;
import java.util.*;
import java.util.function.Supplier;
import java.util.stream.Collectors;
public class CopyUtil extends BeanUtils {

    /**
     * 集合数据的拷贝
     */
    public static <S, T> List<T> copyListNew(List<S> sources, Supplier<T> target, CallBack<S, T> callBack, @Nullable String... ignoreProperties) {
        if (CheckUtil.isNullList(sources)) {
            return new ArrayList<>();
        }
        return copyNewListProperties(sources, target, callBack, ignoreProperties);
    }

    /**
     * 复制对象 不是新对象
     */
    public static <S, T> void copyObject(S source, T target, @Nullable CallBack<S, T> callBack, @Nullable String... ignoreProperties) {
        try {
            if (callBack != null) {
                // 回调
                 var b = callBack.callBack(source, target);
                 if (b!=null && !b){
                     return;
                 }
            }
            List<String> nullString = getNullField(source);
            if (CheckUtil.isNotNullList(nullString) && CheckUtil.isNotNullArray(ignoreProperties)) {
                List<String> ignoreList = new ArrayList<>(Arrays.asList(ignoreProperties));
                ignoreList.addAll(nullString);
                ignoreProperties = nullString.toArray(new String[ignoreList.size()]);
            }
            if (ignoreProperties != null) {
                copyProperties(source, target, ignoreProperties);
            }else{
                copyProperties(source, target);
            }
        } catch (BeansException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     *  复制分页
     */
    public static <S, T> Page<T> copyPage(Page<S> sources, Supplier<T> target, CallBack<S, T> callBack, @Nullable String... ignoreProperties) {
        if (CheckUtil.isNullObject(sources)) {
            return new Page<>(sources.getPages(), sources.getSize());
        }
        return copyPageProperties(sources, target, callBack, ignoreProperties);
    }

    /**
     * 复制对象放到新对象中
     *
     * @Param:
     * @Author: lgl
     * @Date 2022/6/15 18:11
     */
    public static <T, S> T copyObjectNew(S source, Supplier<T> target, CallBack<S, T> callBack, String... ignoreProperties) {
        try {
            if (CheckUtil.isNullObject(source)) {
                throw new RuntimeException("对象不能为空");
            }
            var t = target.get();
            if (callBack != null) {
                var b = callBack.callBack(source, t);
                if (b!=null && !b){
                    return t;
                }
            }
            Set<String> nullString = getNotNullField(t);
            if (!nullString.isEmpty()) {
                Set<String> ignoreList = new HashSet<>(Arrays.asList(ignoreProperties));
                ignoreList.addAll(nullString);
                ignoreProperties = nullString.toArray(new String[ignoreList.size()]);
            }
            copyProperties(source, t, ignoreProperties);
            return t;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 带回调函数的集合数据的拷贝（可自定义字段拷贝规则）
     *
     * @param sources:  数据源类
     * @param target:   目标类::new(eg: UserVO::new)
     * @param callBack: 回调函数
     */

    private static <S, T> List<T> copyNewListProperties(List<S> sources, Supplier<T> target, CallBack<S, T> callBack, @Nullable String... ignoreProperties) {
        List<T> list = new ArrayList<>(sources.size());
        try {
            Set<String> ignoreList;
            if (ignoreProperties != null) {
                ignoreList = Set.of(ignoreProperties);
            } else {
                ignoreList = new HashSet<>();
            }
            for (S s : sources) {
                T t = target.get();
                List<String> ignoreLists = new ArrayList<>(ignoreList);
                ignoreLists.addAll(getNotNullField(t));
                var array = ignoreLists.toArray(new String[0]);
                if (CheckUtil.isNotNullObject(callBack)) {
                     var b = callBack.callBack(s, t);
                     if (b !=null && !b){
                            continue;
                     }
                }
                copyProperties(s, t, array);
                list.add(t);
            };
        } catch (BeansException e) {
            throw new RuntimeException(e);
        }
        return list;
    }
    /**
     * 得到不为空的字段list
     *
     */
    private static Set<String> getNotNullField(Object target) {
        BeanWrapper beanWrapper = new BeanWrapperImpl(target);
        PropertyDescriptor[] propertyDescriptors = beanWrapper.getPropertyDescriptors();
        Set<String> notNullFieldSet = new HashSet<>();
        for (PropertyDescriptor p : propertyDescriptors) {
            String name = p.getName();
            Object value = beanWrapper.getPropertyValue(name);
            if (CheckUtil.isNotNullObject(value)) {
                notNullFieldSet.add(name);
            }
        }
        return notNullFieldSet;
    }

    /**
     * 得到为空的的字段
     *
     * @param target
     */
    private static List<String> getNullField(Object target) {
        BeanWrapper beanWrapper = new BeanWrapperImpl(target);
        PropertyDescriptor[] propertyDescriptors = beanWrapper.getPropertyDescriptors();
        List<String> notNullFieldSet = new ArrayList<>();
        for (PropertyDescriptor p : propertyDescriptors) {
            String name = p.getName();
            Object value = beanWrapper.getPropertyValue(name);
            if (CheckUtil.isNullObject(value)) {
                notNullFieldSet.add(name);
            }
        }
        return notNullFieldSet.stream().distinct().toList();
    }

    /**
     * 拷贝分页
     *
     * @param sources: 数据源类
     * @param target:  目标类::new(eg: UserVO::new)
     */
    private static <S, T> Page<T> copyPageProperties(Page<S> sources, Supplier<T> target, CallBack<S, T> callBack, @Nullable String... ignoreProperties) {
        Page<T> objectPage;
        try {
            var records = sources.getRecords();
            List<T> list = new ArrayList<>(records.size());
            for (S source : records) {
                T t = target.get();
                if (CheckUtil.isNotNullObject(callBack)) {
                     var b = callBack.callBack(source, t);
                     if (b!=null && !b){
                         continue;
                     }
                }
                List<String> nullString = getNullField(source);
                if (CheckUtil.isNotNullList(nullString)) {
                    List<String> ignoreList = new ArrayList<>();
                    if (ignoreProperties != null) {
                        ignoreList = new ArrayList<>(Arrays.asList(ignoreProperties));
                    }
                    ignoreList.addAll(nullString);
                    ignoreProperties = nullString.toArray(new String[ignoreList.size()]);
                }
                if (ignoreProperties != null) {
                    copyProperties(source, t, ignoreProperties);
                }
                list.add(t);
            }
            objectPage = new Page<>(sources.getCurrent(), sources.getSize(), sources.getTotal());
            objectPage.setRecords(list);
        } catch (BeansException e) {
            throw new RuntimeException(e);
        }
        return objectPage;
    }


    /**
     * 合并map
     *
     */
    public static List<Map<String, Object>> mergeMap(List<Map<String, Object>> m1, List<Map<String, Object>> m2, String sameField) {
        try {
            m1.addAll(m2);
            Set<String> set = new HashSet<>();
            return m1.stream().collect(Collectors.groupingBy(o -> {
                //暂存所有key
                set.addAll(o.keySet());
                //按a_id分组
                return o.get(sameField);
            })).entrySet().parallelStream().map(o -> {
                //合并
                Map<String, Object> map = o.getValue().stream().flatMap(m -> m.entrySet().stream()).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> b));
                //为没有的key赋值0
                set.forEach(kt -> {
                    if (!map.containsKey(kt)) {
                        map.put(kt, 0);
                    }
                });
                return map;
            }).toList();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}